use std::borrow::Borrow;
use std::cell::RefCell;
use std::rc::Rc;
use crate::class_file::cf_constant_info::CFConstantInfoEnum;
use crate::class_file::class_file::ClassFile;
use crate::get_constant_info_from_class_file;
use crate::class::vm_class::{VMClass, VMClassField, VMClassMethod};
use crate::vm_thread::VMThread;

pub enum VMClassConstantInfoEnum {
    VMClassConstantInfoInvalid { val: Box<VMClassConstantInfoInvalid> },
    VMClassConstantInfoClass { val: Box<VMClassConstantInfoClass> },
    VMClassConstantInfoString { val: Box<VMClassConstantInfoString> },
    VMClassConstantInfoInteger { val: Box<VMClassConstantInfoInteger> },
    VMClassConstantInfoFieldRef { val: Box<VMClassConstantInfoFieldRef> },
    VMClassConstantInfoMethodRef { val: Box<VMClassConstantInfoMethodRef> },
    VMClassConstantInfoInterfaceMethodRef { val: Box<VMClassConstantInfoInterfaceMethodRef> },
    VMClassConstantInfoFloat { val: Box<VMClassConstantInfoFloat> },
    VMClassConstantInfoLong { val: Box<VMClassConstantInfoLong> },
    VMClassConstantInfoDouble { val: Box<VMClassConstantInfoDouble> },
    VMClassConstantInfoNameAndType { val: Box<VMClassConstantInfoNameAndType> },
    VMClassConstantInfoUTF8 { val: Box<VMClassConstantInfoUTF8> },
    VMClassConstantInfoMethodHandle { val: Box<VMClassConstantInfoMethodHandle> },
    VMClassConstantInfoMethodType { val: Box<VMClassConstantInfoMethodType> },
    VMClassConstantInfoInvokeDynamicInfo { val: Box<VMClassConstantInfoInvokeDynamicInfo> },
}


/// 无效常量
#[derive(Clone)]
pub struct VMClassConstantInfoInvalid {}

pub const VMCLASS_CONSTANT_INFO_INVALID: VMClassConstantInfoInvalid = VMClassConstantInfoInvalid {};

/*
CONSTANT_Class_info {
    u1 tag;
    u2 name_index;
}
*/
#[derive(Clone)]
pub struct VMClassConstantInfoClass {
    pub name: String,
}

/*
CONSTANT_String_info {
    u1 tag;
    u2 string_index;
}
*/
#[derive(Clone)]
pub struct VMClassConstantInfoString {
    pub string: String,
}

/*
CONSTANT_Fieldref_info {
    u1 tag;
    u2 class_index;
    u2 name_and_type_index;
}
CONSTANT_Methodref_info {
    u1 tag;
    u2 class_index;
    u2 name_and_type_index;
}
CONSTANT_InterfaceMethodref_info {
    u1 tag;
    u2 class_index;
    u2 name_and_type_index;
}
*/
#[derive(Clone)]
pub struct VMClassConstantInfoFieldRef {
    pub class_name: String,
    pub field_name: String,
    pub descriptor: String,
    pub vm_class_field: Option<Rc<RefCell<VMClassField>>>,
    pub vm_class: Option<Rc<VMClass>>,
}


impl VMClassConstantInfoFieldRef {
    pub fn init(&mut self, thread: &VMThread) {
        if self.vm_class_field.is_none() {
            let vm_class = {
                let mut class_loader = thread.vm.class_loader.borrow_mut();
                let result = class_loader.load_class(&self.class_name);
                if result.is_err() {
                    panic!("{},{},{}", file!(), line!(), result.err().unwrap());
                }
                result.unwrap()
            };

            let field = {
                let result = vm_class.find_field(&self.field_name, &self.descriptor);
                if result.is_err() {
                    panic!("{}", result.err().unwrap());
                }
                result.unwrap().clone()
            };
            self.vm_class = Some(vm_class);
            self.vm_class_field = Some(field);
        }
    }
}

#[derive(Clone)]
pub struct VMClassConstantInfoMethodRef {
    pub class_name: String,
    pub name_and_type: Box<VMClassConstantInfoNameAndType>,
    pub vm_class_method: Option<Rc<VMClassMethod>>,
    pub vm_class: Option<Rc<VMClass>>,
}

impl VMClassConstantInfoMethodRef {
    pub fn init(&mut self, thread: &VMThread) {
        if self.vm_class_method.is_none() {
            let class_name = &self.class_name;
            let name_type = &self.name_and_type;

            let mut rw_class_loader = thread.vm.class_loader.borrow_mut();
            let load_class_result = rw_class_loader.load_class(&class_name);
            if load_class_result.is_err() {
                panic!("{},{},{}", file!(), line!(), load_class_result.err().unwrap());
            }
            let vm_class = load_class_result.unwrap();

            let method_result = vm_class.find_method(&name_type.name, &name_type.descriptor);
            let method = match method_result {
                Ok(val) => { val }
                Err(msg) => { panic!("{}", msg) }
            };
            self.vm_class_method = Some(method);
            self.vm_class = Some(vm_class);
        }
    }
}

#[derive(Clone)]
pub struct VMClassConstantInfoInterfaceMethodRef {
    cp_class_index: u16,
    name_and_type_index: u16,
}

/*
CONSTANT_Integer_info {
    u1 tag;
    u4 bytes;
}
*/
#[derive(Clone)]
pub struct VMClassConstantInfoInteger {
    pub val: i32,
}

/*
CONSTANT_Float_info {
    u1 tag;
    u4 bytes;
}
*/
#[derive(Clone)]
pub struct VMClassConstantInfoFloat {
    pub val: f32,
}

/*
CONSTANT_Long_info {
    u1 tag;
    u4 high_bytes;
    u4 low_bytes;
}
*/
#[derive(Clone)]
pub struct VMClassConstantInfoLong {
    pub val: i64,
}

/*
CONSTANT_Double_info {
    u1 tag;
    u4 high_bytes;
    u4 low_bytes;
}
*/
#[derive(Clone)]
pub struct VMClassConstantInfoDouble {
    pub val: f64,
}

/*
CONSTANT_NameAndType_info {
    u1 tag;
    u2 name_index;
    u2 descriptor_index;
}
*/
#[derive(Clone)]
pub struct VMClassConstantInfoNameAndType {
    pub name: String,
    pub descriptor: String,
}

/*
CONSTANT_Utf8_info {
    u1 tag;
    u2 length;
    u1 bytes[length];
}
*/
#[derive(Clone)]
pub struct VMClassConstantInfoUTF8 {
    pub length: u16,
    pub string: String,
}

/*
CONSTANT_MethodHandle_info {
    u1 tag;
    u1 reference_kind;
    u2 reference_index;
}
*/
#[derive(Clone)]
pub struct VMClassConstantInfoMethodHandle {
    reference_kind: u8,
    reference_index: u16,
}

/*
CONSTANT_MethodType_info {
    u1 tag;
    u2 descriptor_index;
}
*/
#[derive(Clone)]
pub struct VMClassConstantInfoMethodType {
    descriptor_index: u16,
}

/*
CONSTANT_InvokeDynamic_info {
    u1 tag;
    u2 bootstrap_method_attr_index;
    u2 name_and_type_index;
}
*/
#[derive(Clone)]
pub struct VMClassConstantInfoInvokeDynamicInfo {
    bootstrap_method_attr_index: u16,
    name_and_type_index: u16,
}

pub fn create_vmclass_constant_info(cfconstant_info: &CFConstantInfoEnum, cf: &ClassFile) -> VMClassConstantInfoEnum {
    return match cfconstant_info {
        CFConstantInfoEnum::CFConstantInfoInvalid(_) => {
            VMClassConstantInfoEnum::VMClassConstantInfoInvalid {
                val: Box::new(VMCLASS_CONSTANT_INFO_INVALID)
            }
        }
        CFConstantInfoEnum::CFConstantInfoClass(val) => {
            let constant_info_utf8option = get_constant_info_from_class_file!(*cf, (val.cp_index as usize), CFConstantInfoUTF8);
            if constant_info_utf8option.is_err() {
                panic!("{},{},{}", file!(), line!(), constant_info_utf8option.err().unwrap());
            }
            VMClassConstantInfoEnum::VMClassConstantInfoClass {
                val: Box::new(VMClassConstantInfoClass { name: String::from(constant_info_utf8option.unwrap().string) })
            }
        }
        CFConstantInfoEnum::CFConstantInfoString(val) => {
            let constant_info_utf8option = get_constant_info_from_class_file!(*cf, (val.string_index as usize), CFConstantInfoUTF8);
            if constant_info_utf8option.is_err() {
                panic!("{},{},{}", file!(), line!(), constant_info_utf8option.err().unwrap());
            }
            VMClassConstantInfoEnum::VMClassConstantInfoString {
                val: Box::new(VMClassConstantInfoString {
                    string: constant_info_utf8option.unwrap().string
                })
            }
        }
        CFConstantInfoEnum::CFConstantInfoInteger(cf_val) => {
            VMClassConstantInfoEnum::VMClassConstantInfoInteger {
                val: Box::new(VMClassConstantInfoInteger {
                    val: cf_val.val
                })
            }
        }
        CFConstantInfoEnum::CFConstantInfoFieldRef(cf_val) => {
            let constant_info_class_option = get_constant_info_from_class_file!(
                *cf, (cf_val.cp_class_index as usize), CFConstantInfoClass);
            if constant_info_class_option.is_err() {
                panic!("{},{},{}", file!(), line!(), constant_info_class_option.err().unwrap());
            }
            let constant_info_class = constant_info_class_option.unwrap();

            let constant_info_utf8_option = get_constant_info_from_class_file!(
                *cf, (constant_info_class.cp_index as usize), CFConstantInfoUTF8);
            if constant_info_utf8_option.is_err() {
                panic!("{},{},{}", file!(), line!(), constant_info_utf8_option.err().unwrap());
            }
            let class_name = constant_info_utf8_option.unwrap().string;


            let cf_name_type_option = get_constant_info_from_class_file!(
                *cf, (cf_val.name_and_type_index as usize), CFConstantInfoNameAndType);
            if cf_name_type_option.is_err() {
                panic!("{},{},{}", file!(), line!(), cf_name_type_option.err().unwrap());
            }
            let cf_name_type = cf_name_type_option.unwrap();

            let method_name_option = get_constant_info_from_class_file!(
                *cf, (cf_name_type.name_index as usize), CFConstantInfoUTF8);
            if method_name_option.is_err() {
                panic!("{},{},{}", file!(), line!(), method_name_option.err().unwrap());
            }
            let field_name = method_name_option.unwrap().string;

            let descriptor_option = get_constant_info_from_class_file!(
                *cf, (cf_name_type.descriptor_index as usize), CFConstantInfoUTF8);
            if descriptor_option.is_err() {
                panic!("{},{},{}", file!(), line!(), descriptor_option.err().unwrap());
            }
            let descriptor = descriptor_option.unwrap().string;
            VMClassConstantInfoEnum::VMClassConstantInfoFieldRef {
                val: Box::new(VMClassConstantInfoFieldRef {
                    class_name,
                    field_name,
                    descriptor,
                    vm_class_field: None,
                    vm_class: None,
                })
            }
        }
        CFConstantInfoEnum::CFConstantInfoMethodRef(cf_val) => {
            let cf_class_option = get_constant_info_from_class_file!(
                *cf, (cf_val.cp_class_index as usize), CFConstantInfoClass);
            if cf_class_option.is_err() {
                panic!("{},{},{}", file!(), line!(), cf_class_option.err().unwrap());
            }
            let cf_class = cf_class_option.unwrap();

            let name_option = get_constant_info_from_class_file!(
                *cf, (cf_class.cp_index as usize), CFConstantInfoUTF8);
            if name_option.is_err() {
                panic!("{},{},{}", file!(), line!(), name_option.err().unwrap());
            }
            let class_name = name_option.unwrap().string;

            let name_type_option = get_constant_info_from_class_file!(
                *cf, (cf_val.name_and_type_index as usize), CFConstantInfoNameAndType);
            if name_type_option.is_err() {
                panic!("{},{},{}", file!(), line!(), name_type_option.err().unwrap());
            }
            let cf_name_type = CFConstantInfoEnum::CFConstantInfoNameAndType(name_type_option.unwrap());

            let vm_constant_info_enum = create_vmclass_constant_info(&cf_name_type, &cf);
            let name_and_type = match vm_constant_info_enum {
                VMClassConstantInfoEnum::VMClassConstantInfoNameAndType { val } => {
                    val
                }
                _ => {
                    panic!("{},{},{}", file!(), line!(), "常量池解析异常");
                }
            };

            VMClassConstantInfoEnum::VMClassConstantInfoMethodRef {
                val: Box::new(VMClassConstantInfoMethodRef {
                    class_name,
                    name_and_type,
                    vm_class_method: None,
                    vm_class: None,
                })
            }
        }
        CFConstantInfoEnum::CFConstantInfoInterfaceMethodRef(_) => {
            VMClassConstantInfoEnum::VMClassConstantInfoInvalid {
                val: Box::new(VMCLASS_CONSTANT_INFO_INVALID)
            }
        }
        CFConstantInfoEnum::CFConstantInfoFloat(cf_val) => {
            VMClassConstantInfoEnum::VMClassConstantInfoFloat {
                val: Box::new(VMClassConstantInfoFloat {
                    val: cf_val.val
                })
            }
        }
        CFConstantInfoEnum::CFConstantInfoLong(cf_val) => {
            VMClassConstantInfoEnum::VMClassConstantInfoLong {
                val: Box::new(VMClassConstantInfoLong {
                    val: cf_val.val
                })
            }
        }
        CFConstantInfoEnum::CFConstantInfoDouble(cf_val) => {
            VMClassConstantInfoEnum::VMClassConstantInfoDouble {
                val: Box::new(VMClassConstantInfoDouble {
                    val: cf_val.val
                })
            }
        }
        CFConstantInfoEnum::CFConstantInfoNameAndType(cf_name_type) => {
            let name_option = get_constant_info_from_class_file!(
                *cf, (cf_name_type.name_index as usize), CFConstantInfoUTF8);
            if name_option.is_err() {
                panic!("{},{},{}", file!(), line!(), name_option.err().unwrap());
            }
            let name = name_option.unwrap().string;

            let descriptor_option = get_constant_info_from_class_file!(
                *cf, (cf_name_type.descriptor_index as usize), CFConstantInfoUTF8);
            if descriptor_option.is_err() {
                panic!("{},{},{}", file!(), line!(), descriptor_option.err().unwrap());
            }
            let descriptor = descriptor_option.unwrap().string;
            VMClassConstantInfoEnum::VMClassConstantInfoNameAndType {
                val: Box::new(VMClassConstantInfoNameAndType {
                    name,
                    descriptor,
                })
            }
        }
        CFConstantInfoEnum::CFConstantInfoUTF8(_) => {
            VMClassConstantInfoEnum::VMClassConstantInfoInvalid {
                val: Box::new(VMCLASS_CONSTANT_INFO_INVALID)
            }
        }
        CFConstantInfoEnum::CFConstantInfoMethodHandle(_) => {
            VMClassConstantInfoEnum::VMClassConstantInfoInvalid {
                val: Box::new(VMCLASS_CONSTANT_INFO_INVALID)
            }
        }
        CFConstantInfoEnum::CFConstantInfoMethodType(_) => {
            VMClassConstantInfoEnum::VMClassConstantInfoInvalid {
                val: Box::new(VMCLASS_CONSTANT_INFO_INVALID)
            }
        }
        CFConstantInfoEnum::CFConstantInfoInvokeDynamicInfo(_) => {
            VMClassConstantInfoEnum::VMClassConstantInfoInvalid {
                val: Box::new(VMCLASS_CONSTANT_INFO_INVALID)
            }
        }
    };
}